/**
 * 这个类可以从两方面来考虑。你可以把它看成一个位向量或者一组非负整数。这个名字有点误导人。
 * 
 * 它是由一个位向量实现的，但同样可以把它看成是一个非负整数的集合;集合中的每个整数由对应索引处的集合位表示。该结构的大小由集合中的最大整数决定。
 */
class BitSet{
    private static LONG_MASK: number = 0x3f;
    private _bits: number[];

    constructor(nbits: number = 64){
        let length = nbits >> 6;
        if ((nbits & BitSet.LONG_MASK) != 0)
            length ++;

        this._bits = new Array(length);
    }

    public and(bs: BitSet){
        let max = Math.min(this._bits.length, bs._bits.length);
        let i;
        for (let i = 0; i < max; ++i)
            this._bits[i] &= bs._bits[i];

        while (i < this._bits.length)
            this._bits[i ++] = 0;
    }

    public andNot(bs: BitSet){
        let i = Math.min(this._bits.length, bs._bits.length);
        while(--i >= 0)
            this._bits[i] &= ~bs._bits[i];
    }

    public cardinality(): number{
        let card = 0;
        for (let i = this._bits.length - 1; i >= 0; i --){
            let a = this._bits[i];

            if (a == 0)
                continue;

            if (a == -1){
                card += 64;
                continue;
            }

            a = ((a >> 1) & 0x5555555555555555) + (a & 0x5555555555555555);
            a = ((a >> 2) & 0x3333333333333333) + (a & 0x3333333333333333);
            let b = ((a >> 32) + a);
            b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f);
            b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff);
            card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff);
        }

        return card;
    }

    public clear(pos?: number){
        if (pos != undefined){
            let offset = pos >> 6;
            this.ensure(offset);
            this._bits[offset] &= ~(1 << pos);
        }else{
            for (let i = 0; i < this._bits.length; i ++)
                this._bits[i] = 0;
        }
    }

    private ensure(lastElt: number){
        if (lastElt >= this._bits.length){
            let nd = new Number[lastElt + 1];
            nd = this._bits.copyWithin(0, 0, this._bits.length);
            this._bits = nd;
        }
    }

    public get(pos: number): boolean{
        let offset = pos >> 6;
        if (offset >= this._bits.length)
            return false;

        return (this._bits[offset] & (1 << pos)) != 0;
    }

    public intersects(set: BitSet){
        let i = Math.min(this._bits.length, set._bits.length);
        while (--i >= 0){
            if ((this._bits[i] & set._bits[i]) != 0)
                return true;
        }

        return false;
    }

    public isEmpty(): boolean{
        for (let i = this._bits.length - 1; i >= 0; i --){
            if (this._bits[i])
                return false;
        }

        return true;
    }

    public nextSetBit(from: number){
        let offset = from >> 6;
        let mask = 1 << from;
        while (offset < this._bits.length){
            let h = this._bits[offset];
            do {
                if ((h & mask) != 0)
                    return from;

                mask <<= 1;
                from ++;
            } while (mask != 0);

            mask = 1;
            offset ++;
        }

        return -1;
    }

    public set(pos: number, value: boolean = true){
        if (value){
            let offset = pos >> 6;
            this.ensure(offset);
            this._bits[offset] |= 1 << pos;
        }else{
            this.clear(pos);
        }
    }
}